/***************************************************************************
 *   Copyright (C) 2008 by Philipp Müller                                  *
 *   philipp.mueller@gmx.de                                                *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA          *
 ***************************************************************************/

#include <QtNetwork>
#include <QtGui>

#include "vdr.h"
#include "vompglobal.h"

VDR::VDR( QObject *parent ) : QTcpSocket( parent ),
        m_ringBuffer( 50000 )
{
    m_requestBlock = 1;
    m_streaming = false;

    m_keepAlive = new QTimer( this );
    connect( m_keepAlive, SIGNAL( timeout() ), this, SLOT( sendKeepAlive() ) );

    connect( this, SIGNAL( readyRead() ), this, SLOT( readFromSocket() ) );

    connect( this, SIGNAL( error( QAbstractSocket::SocketError ) ),
             this, SLOT( displayError( QAbstractSocket::SocketError ) ) );

    connect( this, SIGNAL( disconnected() ),
             this, SLOT( gotDisconnect() ) );

    connect( this, SIGNAL( toBeWriten( const QByteArray& ) ),
             this, SLOT( writeToSocket( const QByteArray& ) ) );
}


VDR::~VDR()
{
    qDebug() << "VDR::~VDR called";
}


void VDR::setVDRServerAddress( QHostAddress vdr )
{
    //called from outside
    m_mutex.lock();
    m_VDRAddress = vdr;
    m_mutex.unlock();
}


quint32 VDR::requestPacketHeader( QDataStream & out, quint32 messageID, quint32 packageID )
{
    //called from outside, but already within a mutex
    out << ( quint32 )( CHANNEL_REQUEST_RESPONSE );
    out << ( quint32 )( m_requestBlock ); //RequestID
    out << ( quint32 )( messageID ); //MessageID

    m_requestsHash.insert( m_requestBlock, QPair<quint32, quint32>( messageID, packageID ) );
    m_requestBlock++;
    return m_requestBlock - 1;
}


void VDR::writeToSocket( const QByteArray& array )
{
    //called from inside

    //We send something to the server, so reset the keep alive timer
    m_keepAlive->start( 5000 );

    qint64 writen;
    writen = write( array );
#ifdef NETWORK_DEBUG
    qDebug() << "VDR::writeToSocket called and we wrote " << writen;
#endif
}


void VDR::readFromSocket()
{
#ifdef NETWORK_DEBUG
    qDebug() << "VDR::readFromSocket with bytesAvailable: " << bytesAvailable();
#endif

    while( bytesAvailable() >= readMinPacketLength() )
    {
        quint32 channel;
        QDataStream in( this );
        in >> channel;

#ifdef NETWORK_DEBUG
        qDebug() << "VDR::readFromSocket channel = " << channel;
#endif

        QPair<quint32, quint32> hashData;

        switch ( channel )
        {

            case CHANNEL_KEEPALIVE:
                quint32 timeStamp;
                in >> timeStamp;
#ifdef NETWORK_DEBUG
                qDebug() << "VDR::readSocket -read timeStamp" << timeStamp;
#endif
                return;

            case CHANNEL_STREAM:
                readStream( in );
                return;

            case CHANNEL_REQUEST_RESPONSE:
                quint32 requestID;
                quint32 messageID;
                quint32 blockSize;
                in >> messageID;
                in >> blockSize; //userDataLength

#ifdef NETWORK_DEBUG
                QHashIterator<quint32, QPair<quint32, quint32> > i( m_requestsHash );
                while ( i.hasNext() )
                {
                    i.next();
                    qDebug() << "VDR:requestHash: i: " << i.key() << ": " << i.value().first;
                }
#endif

                hashData = m_requestsHash.value( messageID,
                                                QPair<quint32, quint32>( 0, 0 )
                                                /* 0 = default value in case no entry */ );

                requestID = hashData.first;

                if ( requestID == 0 ) //not found
                {
                    qWarning() << "VDR::readSocket: Packet received but no entry in hash table for messageID: " << messageID;
                    return;
                }

                m_requestsHash.remove( messageID );

#ifdef NETWORK_DEBUG
                    qDebug() << "VDR::readSocket packet arrived: channel: " << channel
                            << "  VDR::RequestID: " << requestID
                            << "  VDR::PackageID: " << hashData.second
                            << "  blockSize: " << blockSize;
#endif

                switch ( requestID )
                {

                    case VDR_LOGIN:
                        readLogin( in );
                        break;

                    case VDR_CONFIGLOAD:
                        readConfigLoad( in );
                        break;

                    case VDR_CONFIGSAVE:
                        readConfigSave( in );
                        break;

                    case VDR_GETCHANNELLIST:
                        readChannelList( in, hashData.second, blockSize );
                        break;

                    case VDR_GETCHANNELSCHEDULE:
                        readChannelSchedule( in, hashData.second, blockSize );
                        break;

                    case VDR_STREAMCHANNEL:
                        readStreamChannelDone( in );
                        break;

                    case VDR_STOPSTREAMING:
                        readStopStreaming( in );
                        break;

                    case VDR_GETRECORDINGLIST:
                        readRecordingsList( in, blockSize );
                        break;

                    case VDR_STREAMRECORDING:
                        readRecordingOpen( in );
                        break;

                    case VDR_GETMEDIALIST:
                        readMediaList( in, blockSize );
                        break;

                    case VDR_GETIMAGE:
                        readMediaFileOpen( in );
                        break;

                    case VDR_GETIMAGEBLOCK:
                        readMediaBlock( messageID, blockSize );
                        break;

                    case VDR_GETBLOCK:
                        readRecordingBlock( messageID, blockSize );
                        break;

                    default:
                        qWarning() << "VDR::readSocket: Error! requestID is unknown. requestID: " << requestID;
                        break;
                }
                break;

            default:
                qWarning() << "VDR::readSocket: Error! channel is unknown. channel: " << channel;
                break;
        }
    }
#ifdef NETWORK_DEBUG
    qDebug() << "VDR::readSocket -end: BytesAvailable (should be 0 now): " << bytesAvailable();
#endif
}


quint32 VDR::readMinPacketLength()
{
#ifdef NETWORK_DEBUG
    qDebug() << "VDR::readMinPacketLength: bytesAvailable: " << bytesAvailable();
#endif
    if ( bytesAvailable() < 2 * sizeof( quint32 ) ) // Smallest is keepAlive with 2 quint32
    {
        return 2 * sizeof( quint32 );
    }

    //We make a copy of the current available bytes
    QByteArray array = peek( 4 * sizeof( quint32 ) );
    quint32 length = 0;
    quint32 channel;

    QDataStream in( array );

#ifdef NETWORK_DEBUG
    qDebug() << "VDR::readMinPacketLength: in: " << array.size();
#endif

    in >> channel;

    switch ( channel )
    {

        case CHANNEL_KEEPALIVE:
            length =  2 * sizeof( quint32 );
            break;

        case CHANNEL_STREAM:
            if( (quint32) array.size() < 4 * sizeof( quint32 ) )
            {
                //too small
                return 4 * sizeof( quint32 );
            }
            quint32 streamID;
            quint32 streamFlag;
            quint32 streamLength;

            in >> streamID;
            in >> streamFlag;
            in >> streamLength;

            length = 3 * sizeof( quint32 ) + streamLength;
            break;

        case CHANNEL_REQUEST_RESPONSE:
            if( (quint32) array.size() < 3 * sizeof( quint32 ) )
            {
                //too small
                return 3 * sizeof( quint32 );
            }
            quint32 messageID;
            quint32 userDataLength;

            in >> messageID;
            in >> userDataLength;

            length = 2 * sizeof( quint32 ) + userDataLength;
            break;

        default:
            qWarning() << "VDR::readMinPacketLength: channel type unknown: " << channel;
            length = 2 * sizeof( quint32 );

    }
    return length;
}


void VDR::requestLogin()
{
    //Called via slot
    qDebug() << "VDR::requestLogin: " << m_VDRAddress.toString();

    abort();
    connectToHost( m_VDRAddress, 3024 );

    QByteArray array;
    QDataStream out( &array, QIODevice::WriteOnly );

    requestPacketHeader( out, VDR_LOGIN );
    out << ( quint32 )( 6 ); //User data length: MAC = 6

    QString macAddress = getMacAddress();
    bool ok;

    for ( uint i = 0; i < 18; )
    {
        out << ( quint8 )( macAddress.mid( i, 2 ).toUInt( &ok, 16 ) );
        i = i + 3; // 2 hex characters plus a ":"
    }

    emit toBeWriten( array );

#ifdef NETWORK_DEBUG
    qDebug() << "VDR::requestLogin requested";
#endif
}


void VDR::sendKeepAlive()
{
    //called from inside
    qDebug() << "VDR::sendKeepAlive time: " << QTime::currentTime();

    //reset this timer
    m_keepAlive->start( 5000 );

    QByteArray array;
    QDataStream out( &array, QIODevice::WriteOnly );

    out << ( quint32 )( CHANNEL_KEEPALIVE );
    out << ( quint32 )( QDateTime::currentDateTime().toTime_t() ); //RequestID

    emit toBeWriten( array );
}


void VDR::getChannelList( quint32 type )
{
    //called from inside
    qDebug() << "VDR::getChannelList";

    QByteArray array;
    QDataStream out( &array, QIODevice::WriteOnly );

    requestPacketHeader( out, VDR_GETCHANNELLIST, type );
    out << ( quint32 )( 0 ); //User data length

    emit toBeWriten( array );
}


void VDR::getChannelSchedule( quint32 channel )
{
    getChannelSchedule( channel, QDateTime::currentDateTime(), 24 * 60 * 60 );
}


void VDR::getChannelSchedule( quint32 channel, QDateTime dateTime, quint32 duration )
{
    //called through slot therefore from inside
#ifdef NETWORK_DEBUG
    qDebug() << "VDR::getChannelSchedule with channel:" << channel;
#endif

    QByteArray array;
    QDataStream out( &array, QIODevice::WriteOnly );

    requestPacketHeader( out, VDR_GETCHANNELSCHEDULE, channel );
    out << ( quint32 )( sizeof( quint32 ) + sizeof( quint32 ) + sizeof( quint32 ) ); //User data length
    out << ( quint32 )( channel );
    out << ( quint32 )( dateTime.addSecs( m_serverTimeOffset ).toTime_t() );
    out << ( quint32 )( duration );

    emit toBeWriten( array );
}


quint32 VDR::streamChannel( quint32 channel )
{
#ifdef NETWORK_DEBUG
    qDebug() << "VDR::streamChannel called Channel:" << channel;
#endif

    //Called from outside
    m_mutex.lock();

    m_streaming = true;
    m_streamResult = 0;

    //Request the package
    QByteArray array;
    QDataStream out( &array, QIODevice::WriteOnly );

    requestPacketHeader( out, VDR_STREAMCHANNEL );
    out << ( quint32 )( 4 ); //User data length
    out << channel;

    emit toBeWriten( array );

#ifdef NETWORK_DEBUG
    qDebug() << "VDR::streamChannel: Request package was written, now waiting for answer.";
#endif

    //Wait for Open
    m_waitingForStreamStarted.wait( &m_mutex );

    m_mutex.unlock();

#ifdef NETWORK_DEBUG
    qDebug() << "VDR::streamChannel: Stream wait condition was released";
#endif

    return m_streamResult;
}


quint32 VDR::stopStreaming()
{
    qDebug() << "VDR::stopStreaming";

    if( !m_streaming )
    {
        return 0; //TODO Check the correct return value of stopStreaming
    }

    //Called from outside
    m_mutex.lock();

    m_streaming = false;
    m_streamResult = 0;

    QByteArray array;
    QDataStream out( &array, QIODevice::WriteOnly );

    requestPacketHeader( out, VDR_STOPSTREAMING );
    out << ( quint32 )( 0 ); //User data length

    emit toBeWriten( array );

    //Wait for stream stopped
    m_waitingForStreamStopped.wait( &m_mutex );

    m_mutex.unlock();

    return m_streamResult;
}


qint64 VDR::readDataFromBuffer( char * data, qint64 maxSize )
{
#ifdef NETWORK_DEBUG
    qDebug() << "VDR::readDataFromBuffer start with buffersize" << m_ringBuffer.size();
#endif

    if ( !m_streaming && m_ringBuffer.size() == 0 )
    {
        qWarning() << "VDR::readDataFromBuffer: Tried to read from buffer while not streaming";
        return 0;
    }

    qint64 read = 0;

    m_mutex.lock();
    read = m_ringBuffer.read( data, maxSize );
    m_mutex.unlock();

#ifdef NETWORK_DEBUG
    qDebug() << "VDR::readDataFromBuffer we read: " << read;
#endif
    return read;
}


qint64 VDR::streamBytesAvailable()
{
    m_mutex.lock();
    qint64 _size = m_ringBuffer.size();
    m_mutex.unlock();
    return _size;
}


bool VDR::waitForStreamReadyRead( int msecs )
{
#ifdef NETWORK_DEBUG
    qDebug() << "VDR::waitForStreamReadyRead started";
#endif

    if ( !m_streaming )
    {
        return false;
    }

    m_mutex.lock();

    m_waitingForStreamReadyRead.wait( &m_mutex );

    m_mutex.unlock();

    return true;
}


bool VDR::isStreaming()
{
    //Called from outside
    bool streaming;

    m_mutex.lock();
    streaming = m_streaming;
    m_mutex.unlock();

    return streaming;
}


QString VDR::loadConfig( const QString& section, const QString& key )
{
    qDebug() << "VDR::loadConfig: Section:" << section << "  key:" << key;

    if( section.isEmpty() || key.isEmpty() )
    {
        qWarning() << "VDR::loadConfig: One of both is empty! Section:" << section << "  key:" << key;
        return QString();
    }

    //Called from outside
    m_mutex.lock();

    m_ConfigEntry.clear();

    //Request the package
    QByteArray array;
    QDataStream out( &array, QIODevice::WriteOnly );

    requestPacketHeader( out, VDR_CONFIGLOAD );
    out << ( quint32 )( section.length() + 1 + key.length() + 1 ); //User data length

    if ( !section.isEmpty() )
    {
        writeString( out, section );
    }

    if ( !key.isEmpty() )
    {
        writeString( out, key );
    }

    emit toBeWriten( array );

    //Wait for Read
    m_waitingForConfigDone.wait( &m_mutex );

    QString toReturn = m_ConfigEntry;

    m_mutex.unlock();

    return toReturn;
}


bool VDR::saveConfig( const QString& section, const QString& key, const QString& value )
{
    qDebug() << "VDR::saveConfig: Section:" << section << "  key:" << key;

    if( section.isEmpty() || key.isEmpty() || value.isEmpty() )
    {
        qWarning() << "VDR::saveConfig: One of entries is empty! Section:" << section << "  key:" << key << "  value: "  << value;
        return false;
    }

    //Called from outside
    m_mutex.lock();

    m_ConfigSave = false;

    //Request the package
    QByteArray array;
    QDataStream out( &array, QIODevice::WriteOnly );

    requestPacketHeader( out, VDR_CONFIGSAVE );
    out << ( quint32 )( section.length() + 1 + key.length() + 1 + value.length() + 1 ); //User data length

    writeString( out, section );
    writeString( out, key );
    writeString( out, value );

    emit toBeWriten( array );

    //Wait for Read
    m_waitingForConfigDone.wait( &m_mutex );

    bool toReturn = m_ConfigSave;

    m_mutex.unlock();

    return toReturn;
}


void VDR::getRecordingsList()
{
    qDebug() << "VDR::getRecordingsList: " << m_VDRAddress.toString();

    QByteArray array;
    QDataStream out( &array, QIODevice::WriteOnly );

    requestPacketHeader( out, VDR_GETRECORDINGLIST );
    out << ( quint32 )( 0 ); //User data length

    emit toBeWriten( array );
}


quint64 VDR::openRecording( const QString& file )
{
#ifdef NETWORK_DEBUG
    qDebug() << "VDR::openRecording: " << file;
#endif

    //Called from outside
    m_mutex.lock();

    m_RecordingSize = 0;

    //Request the package
    QByteArray array;
    QDataStream out( &array, QIODevice::WriteOnly );

    requestPacketHeader( out, VDR_STREAMRECORDING );
    out << ( quint32 )( file.length() + 1 ); //User data length

    if ( !file.isEmpty() )
    {
        writeString( out, file );
    }

    emit toBeWriten( array );

#ifdef NETWORK_DEBUG
    qDebug() << "VDR::openRecording: Request package was written, now waiting for answer.";
#endif

    //Wait for Open
    m_waitingForRecordingOpen.wait( &m_mutex );

    m_mutex.unlock();

#ifdef NETWORK_DEBUG
    qDebug() << "VDR::openMediaFile: Open wait condition was released";
#endif

    return m_RecordingSize;
}


void VDR::requestMediaList( const QString& dir )
{
    qDebug() << "VDR::requestMediaList: " << m_VDRAddress.toString();

    QByteArray array;
    QDataStream out( &array, QIODevice::WriteOnly );

    requestPacketHeader( out, VDR_GETMEDIALIST );

    int dirLength;

    if ( !dir.isEmpty() )
    {
        dirLength = dir.length() + 1; //+ dirlength + /0
    }
    else
    {
        dirLength = 0;
    }

    out << ( quint32 )( 4 + dirLength ); //UserDataLength Flags + dirlength

    out << ( quint32 )( 0 ); //Unused flags

    if ( !dir.isEmpty() )
    {
        writeString( out, dir );
    }

    emit toBeWriten( array );
}


quint64 VDR::openMediaFile( const QString& file, quint32 x, quint32 y )
{
#ifdef NETWORK_DEBUG
    qDebug() << "VDR::openMediaFile called for file " << file;
#endif

    //Called from outside
    m_mutex.lock();

    m_fileSize = 0;

    //Request the package
    QByteArray array;
    QDataStream out( &array, QIODevice::WriteOnly );

    quint32 temp = 0;

    int fileStringLength;

    if ( !file.isEmpty() )
    {
        fileStringLength = file.length() + 1; //+ dirlength + /0
    }
    else
    {
        fileStringLength = 0;
    }

    requestPacketHeader( out, VDR_GETIMAGE );

    out << ( quint32 )( sizeof( temp ) + sizeof( x ) + sizeof( y ) + fileStringLength ); //User data length

    out << temp; //unused flags
    out << x;
    out << y;

    if ( !file.isEmpty() )
    {
        writeString( out, file );
    }

    emit toBeWriten( array );

#ifdef NETWORK_DEBUG
    qDebug() << "VDR::openMediaFile: Request package was written, now waiting for answer.";
#endif

    //Wait for Open
    m_waitingForFileOpen.wait( &m_mutex );

    m_mutex.unlock();

#ifdef NETWORK_DEBUG
    qDebug() << "VDR::openMediaFile: Open wait condition was released";
#endif

    return m_fileSize;
}


qint64 VDR::readMedia( char * data, qint64 maxSize, quint64 position )
{
#ifdef NETWORK_DEBUG
    qDebug() << "VDR::readMedia: position: " << position << "  maxSize: " << maxSize;
#endif

    //Called from outside
    m_mutex.lock();

    qint32 mediaBlockMaxSize = ( qint32 ) maxSize;

    QByteArray array;
    QDataStream out( &array, QIODevice::ReadWrite );

    quint32 packageID = requestPacketHeader( out, VDR_GETIMAGEBLOCK, 0 );

    requestResponseEntry entry;
    entry.data = data; /* 0 = default value in case no entry */
    entry.fileSize = 0;
    entry.mediaBlockMaxSize = mediaBlockMaxSize;
    entry.mediaWriten = 0;
    entry.writen = false;

    m_readHash.insert( packageID, entry );

    out << ( quint32 )( sizeof( position ) + sizeof( mediaBlockMaxSize ) ); //User data length

    out << position;
    out << mediaBlockMaxSize;

    emit toBeWriten( array );

#ifdef NETWORK_DEBUG
    qDebug() << "VDR::readMedia: Request package was written, now waiting for answer.";
#endif

    while ( !m_readHash[ packageID ].writen )
    {
        //Wait for Open
        m_waitingForMediaRead.wait( &m_mutex );
    }

#ifdef NETWORK_DEBUG
    qDebug() << "VDR::readMedia: read wait condition was released";
#endif

    qint64 toReturn = m_readHash[ packageID ].mediaWriten;

    m_readHash.remove( packageID );

    m_mutex.unlock();

    return toReturn;
}


qint64 VDR::readRecording( char * data, qint64 maxSize, quint64 position )
{
#ifdef NETWORK_DEBUG
    qDebug() << "VDR::readRecording: position: " << position << "  maxSize: " << maxSize;
#endif

    //Called from outside
    m_mutex.lock();

    qint32 mediaBlockMaxSize = ( qint32 ) maxSize;

    QByteArray array;
    QDataStream out( &array, QIODevice::ReadWrite );

    quint32 packageID = requestPacketHeader( out, VDR_GETBLOCK, 0 );

    requestResponseEntry entry;
    entry.data = data; /* 0 = default value in case no entry */
    entry.fileSize = 0;
    entry.mediaBlockMaxSize = mediaBlockMaxSize;
    entry.mediaWriten = 0;
    entry.writen = false;

    m_readHash.insert( packageID, entry );

    out << ( quint32 )( sizeof( position ) + sizeof( mediaBlockMaxSize ) ); //User data length

    out << position;
    out << mediaBlockMaxSize;

    emit toBeWriten( array );

#ifdef NETWORK_DEBUG
    qDebug() << "VDR::readRecording: Request package was written, now waiting for answer.";
#endif

    while ( !m_readHash[ packageID ].writen )
    {
        //Wait for Open
        m_waitingForRecordingRead.wait( &m_mutex );
    }

#ifdef NETWORK_DEBUG
    qDebug() << "VDR::readRecording: read wait condition was released";
#endif

    qint64 toReturn = m_readHash[ packageID ].mediaWriten;

    m_readHash.remove( packageID );

    m_mutex.unlock();

    return toReturn;
}


void VDR::requestMediaBlock( quint64 position, qint32 maxAmount, quint32 packageID, quint32 cmd )
{
    qDebug() << "VDR::requestMediaBlock: position: " << position << "  maxAmount: " << maxAmount;

    QByteArray array;
    QDataStream out( &array, QIODevice::ReadWrite );

    requestPacketHeader( out, cmd, packageID );
    out << ( quint32 )( sizeof( position ) + sizeof( maxAmount ) ); //User data length

    out << position;
    out << maxAmount;

    emit toBeWriten( array );
}


bool VDR::readString( QDataStream& in, QString &string )
{
    int b = bytesAvailable();

    for ( int i = 0; i < b; i++ )
    {
        quint8 a;
        in >> a;

        if ( a == 0 )
            return true;

        string.append( a );

//        qDebug() << "VDR::readString: in-value ("<<i<<"): " << a <<"  text" << QString((QChar)a);
    }

    return false;
}


bool VDR::writeString( QDataStream& out, const QString &string )
{
    int b = string.length();

    for ( int i = 0; i < b; i++ )
    {
        quint8 a;
        a = string[i].toLatin1();

        out << a;
    }

    out << ( quint8 )( 0 );

    return true;
}


void VDR::readLogin( QDataStream& in )
{
    quint32 vdrTime;
    qint32 vdrTimeOffset;

    in >> vdrTime;
    in >> vdrTimeOffset;

    m_serverTimeOffset = vdrTime/* + vdrTimeOffset*/ - QDateTime::currentDateTime().toTime_t();

    emit serverTimeOffset( m_serverTimeOffset );

    qDebug() << "VDR::readLogin: Time offset of server: " << m_serverTimeOffset;
}


void VDR::readConfigLoad( QDataStream& in )
{
    readString( in, m_ConfigEntry );

    m_mutex.lock();
    m_waitingForConfigDone.wakeAll();
    m_mutex.unlock();

    qDebug() << "VDR::readConfig: " << m_ConfigEntry;
}


void VDR::readConfigSave( QDataStream& in )
{
    qint32 result;
    in >> result;

    m_ConfigSave = (bool)result;

    m_mutex.lock();
    m_waitingForConfigDone.wakeAll();
    m_mutex.unlock();

    qDebug() << "VDR::readConfig: " << result;
}


void VDR::readChannelList( QDataStream& in, quint32 typeFilter, quint32 length )
{
    QList<channelEntry> channelList;

    while ( length >= 2 * sizeof( quint32 ) )
    {
        quint32 channel;
        quint32 type;
        QString name;

        in >> channel;
        in >> type;

        bool result;
        result = readString( in, name );

#ifdef NETWORK_DEBUG
        qDebug() << "VDR::readChannelList: Adding channel:" << channel
        << "  name: " << name << "  with type=" << type << "  and filter = " << typeFilter;
#endif

        if ( type == typeFilter )
        {
            channelEntry entry;
            entry.name = name;
            entry.type = type;
            entry.channel = channel;
            channelList.append( entry );
        }

        quint32 bytesConsumed = 2 * sizeof( quint32 ) + name.size() + 1;
        if ( bytesConsumed > length )
        {
            length = 0; //unsigned int! Better safe than sorry
        }
        else
        {
            length = length - bytesConsumed;
        }
    }

    emit channelListAvailable( channelList );

}


void VDR::readChannelSchedule( QDataStream& in, quint32 channel, quint32 length )
{
#ifdef NETWORK_DEBUG
    qDebug() << "VDR::readChannelSchedule with channel:" << channel << " and length:" << length;
#endif

    QList<channelScheduleEntry> scheduleList;

    if ( length == 4 ) // Just one empty entry
    {
        channelScheduleEntry entry;

        in >> entry.id;
        entry.duration = 0;
        entry.time = QDateTime();

        scheduleList.append( entry );
    }
    else
    {
        while ( length >= 3 * sizeof( quint32 ) )
        {
            channelScheduleEntry entry;
            quint32 time;

            in >> entry.id;
            in >> time;
            in >> entry.duration;
            readString( in, entry.title );
            readString( in, entry.subTitle );
            readString( in, entry.description );

            entry.time = QDateTime::fromTime_t( time );

            scheduleList.append( entry );

            quint32 bytesConsumed = 3 * sizeof( quint32 ) +
                                    ( entry.title.size() + 1 ) +
                                    ( entry.subTitle.size() + 1 ) +
                                    ( entry.description.size() + 1 );
            if ( bytesConsumed > length )
            {
                length = 0; //unsigned int! Better safe than sorry
            }
            else
            {
                length = length - bytesConsumed;
            }
        }
    }

    emit channelScheduleAvailable( channel, scheduleList );
}


void VDR::readStream( QDataStream& in )
{
    quint32 streamID;
    quint32 streamFlag;
    quint32 streamLength;

    in >> streamID;
    in >> streamFlag;
    in >> streamLength;

#ifdef NETWORK_DEBUG
    qDebug() << "VDR::readStream: streamID: " << streamID << "  flag: " << streamFlag << "  length: " << streamLength;
#endif

    QByteArray array;
    array = read( streamLength );

#ifdef NETWORK_DEBUG
    qDebug() << "VDR::readStream: length: " << array.size();
#endif

    m_mutex.lock();

    m_ringBuffer.write( array );
#ifdef NETWORK_DEBUG
    qDebug() << "VDR::readStream ringBufferSize = " << m_ringBuffer.size();
#endif
    // 0 = stream, 1 = end of stream, 2 connection lost
    if( streamFlag == 1 )
    {
        qDebug() << "VDR::readStream end of stream signaled with streamLength: " << streamLength;
        m_streaming = false;
    }
    else
    {
        emit streamReadyRead();
    }
    m_waitingForStreamReadyRead.wakeAll();

    m_mutex.unlock();
}


void VDR::readStreamChannelDone( QDataStream& in )
{
    in >> m_streamResult;

    m_mutex.lock();
    m_waitingForStreamStarted.wakeAll();
    m_mutex.unlock();

    qDebug() << "VDR::readStreamChannelDone: result: " << m_streamResult;
}


void VDR::readStopStreaming( QDataStream& in )
{
    quint32 result;
    in >> result;

    m_mutex.lock();
    m_waitingForStreamStopped.wakeAll();
    m_mutex.unlock();

    if ( !m_streaming )
    {
        m_ringBuffer.clear();
    }

    qDebug() << "VDR::readStopStreaming: result: " << result;
}


void VDR::readRecordingsList( QDataStream& in, quint32 length )
{
    quint32 totalSpace;
    quint32 freeSpace;
    quint32 percent;
    QList<recordingsEntry> recordingsList;

    in >> totalSpace;
    in >> freeSpace;
    in >> percent;

    qDebug() << "VDR::readRecordingsList: totalSpace: " << totalSpace
             << "  freeSpace: " << freeSpace
             << "  percent: " << percent;


    while ( length >= sizeof( quint32 ) )
    {
        quint32 start;
        QString name;
        QString fileName;

        in >> start;

        bool result;
        result = readString( in, name );
        result = readString( in, fileName );

        quint32 bytesConsumed = sizeof( quint32 ) + name.size() + 1 + fileName.size() + 1;
        if ( bytesConsumed > length )
        {
            length = 0; //unsigned int! Better safe than sorry
        }
        else
        {
            length = length - bytesConsumed;
        }

        if ( name.isEmpty() )
        {
            continue;
        }

        QDateTime time;
        time.setTime_t( start );

        recordingsEntry entry;
        entry.name = name;
        entry.fileName = fileName;
        entry.start = time;
        recordingsList.append( entry );

    }

    emit recordingsAvailable( recordingsList );
}


void VDR::readRecordingOpen( QDataStream& in )
{
//     quint64 length;
    quint32 frames;

    in >> m_RecordingSize;
    in >> frames; //Not needed for Phonon

    m_mutex.lock();

//     m_fileSize = length;
    m_waitingForRecordingOpen.wakeAll();

    m_mutex.unlock();
    qDebug() << "VDR::readRecordingOpen: fileLength: " << m_RecordingSize;
}


void VDR::readMediaList( QDataStream& in, quint32 length )
{
    quint32 code;
    quint32 numEntries;
    QList<mediaEntry> mediaList;

    in >> code;
    in >> numEntries;

    qDebug() << "VDR::readMediaList: code: " << code << "  numEntries: " << numEntries;
    quint32 temp;

    while ( ( length >= 3 * sizeof( quint32 ) ) && numEntries > 0 )
    {
        quint32 type;
        quint32 time;
        quint32 flags;
        QString name;

        in >> type;
        in >> time;
        in >> flags;
        in >> temp; //unused length check

        bool result;
        result = readString( in, name );

        quint32 bytesConsumed = 3 * sizeof( quint32 ) + name.size() + 1;
        if ( bytesConsumed > length )
        {
            length = 0; //unsigned int! Better safe than sorry
        }
        else
        {
            length = length - bytesConsumed;
        }

        if ( name == "." || name == ".." )
            continue;

        //Picture we don't support yet, so ignore these entries as well as the unknown ones.
        if ( type == MEDIA_TYPE_UNKNOWN || type == MEDIA_TYPE_PICTURE ) //Should we really handle these unknown ones?
            continue;

        QDateTime dateTime;
        dateTime.setTime_t( time );

        mediaEntry entry;

        entry.type = type;
        entry.time = dateTime;
        entry.flags = flags;
        entry.name = name;

        mediaList.append( entry );

        numEntries--;
    }

    emit mediaListAvailable( mediaList );
}


void VDR::readMediaFileOpen( QDataStream& in )
{
    //Called by inside
    quint32 cmd;
    quint32 length;

    in >> cmd;
    in >> length;

    m_mutex.lock();

    m_fileSize = length;
    m_waitingForFileOpen.wakeAll();

    m_mutex.unlock();

    qDebug() << "VDR::readOpenMediaFile: fileLength: " << length;
}


void VDR::readMediaBlock( quint32 packageID, quint32 length )
{
#ifdef NETWORK_DEBUG
    qDebug() << "VDR::readMediaBlock called with blockSize:" << length;
#endif

    requestResponseEntry & entry = m_readHash[ packageID ];

    m_mutex.lock();

    if ( entry.data == 0L ) //not found
    {
        qWarning() << "VDR::readMediaBlock was requested, but no read request is there for it. PackageID = " << packageID;
        entry.mediaWriten = 0;
        entry.writen = true;
        m_waitingForMediaRead.wakeAll();
        m_mutex.unlock();
        return;
    }

    entry.mediaWriten = qMin( length, entry.mediaBlockMaxSize );

    // Read from the socket, store data in the read memory.
    qint64 readBytes = read( entry.data, entry.mediaWriten );

    if ( length > entry.mediaBlockMaxSize )
    {
        qWarning() << "VDR::readMediaBlock MediaBlock from VDR is bigger than requested!" <<
        "Throwing data away for the moment as blocksize is:" << entry.mediaBlockMaxSize;
        QByteArray throwAway = read( length - entry.mediaBlockMaxSize );
    }

    if ( readBytes == -2 )
    {
        // No bytes currently available for reading.
        qWarning() << "VDR::readMediaBlock Nothing to read at the moment!";
        entry.mediaWriten = 0;
    }

    entry.writen = true;

    m_waitingForMediaRead.wakeAll();

    m_mutex.unlock();

#ifdef NETWORK_DEBUG
    qDebug() << "VDR::readMediaBlock: all released and readBytes: " << readBytes;
#endif
}


void VDR::readRecordingBlock( quint32 packageID, quint32 length )
{
#ifdef NETWORK_DEBUG
    qDebug() << "VDR::readRecordingBlock called with blockSize:" << length;
#endif

    requestResponseEntry & entry = m_readHash[ packageID ];

    m_mutex.lock();

    if ( entry.data == 0L ) //not found
    {
        qWarning() << "VDR::readRecordingBlock was requested, but no read request is there for it. PackageID = " << packageID;
        entry.mediaWriten = 0;
        entry.writen = true;
        m_waitingForRecordingRead.wakeAll();
        m_mutex.unlock();
        return;
    }

    entry.mediaWriten = qMin( length, entry.mediaBlockMaxSize );

    // Read from the socket, store data in the read memory.
    qint64 readBytes = read( entry.data, entry.mediaWriten );

    if ( length > entry.mediaBlockMaxSize )
    {
        qWarning() << "VDR::readRecordingBlock MediaBlock from VDR is bigger than requested!" <<
        "Throwing data away for the moment as blocksize is:" << entry.mediaBlockMaxSize;
        QByteArray throwAway = read( length - entry.mediaBlockMaxSize );
    }

    if ( readBytes == -2 )
    {
        // No bytes currently available for reading.
        qWarning() << "VDR::readRecordingBlock Nothing to read at the moment!";
        entry.mediaWriten = 0;
    }

    entry.writen = true;

    m_waitingForRecordingRead.wakeAll();

    m_mutex.unlock();

#ifdef NETWORK_DEBUG
    qDebug() << "VDR::readRecordingBlock: all released and readBytes: " << readBytes;
#endif
}


void VDR::displayError( QAbstractSocket::SocketError socketError )
{
    switch ( socketError )
    {

        case QAbstractSocket::RemoteHostClosedError:
            qDebug() << "VDRError: Remote host closed";
            break;

        case QAbstractSocket::HostNotFoundError:
            /*        QMessageBox::information(0L, tr("Qt for Vomp"),
                                             tr("The host was not found. Please check the "
                                                "host name and port settings."));*/
            qDebug() << "VDRError: Remote host not found";
            break;

        case QAbstractSocket::ConnectionRefusedError:
            /*        QMessageBox::information(0L, tr("Qt for Vomp"),
                                             tr("The connection was refused by the peer. "
                                                "Make sure the fortune server is running, "
                                                "and check that the host name and port "
                                                "settings are correct."));*/
            qDebug() << "VDRError: Connection refused";
            break;

        default:
            /*        QMessageBox::information(0L, tr("Qt for Vomp"),
                                             tr("The following error occurred: %1.")
                                             .arg( errorString() ));*/
            qDebug() << "VDRError: Error occured: " <<  errorString();
    }
}


void VDR::gotDisconnect()
{
    qDebug() << "VDR: Disconnection occured";
}


QString VDR::getMacAddress()
{
    QList<QHostAddress> addrlist = QNetworkInterface::allAddresses();
    int o = -1;
    QString returnMAC = 0L;
    QString mach = 0L;
    foreach( QHostAddress addr, addrlist )
    {
        o++;
        QNetworkInterface netIFace = QNetworkInterface::interfaceFromIndex( o );
        mach = netIFace.hardwareAddress();
//        qDebug() << "MAC Address: " << mach << "  IP: " << addr << "  hostaddress: " << vdrTCPSocket->localAddress();

        if (( addr.toIPv4Address()         & QHostAddress( "255.255.255.0" ).toIPv4Address() ) ==
                ( m_VDRAddress.toIPv4Address() & QHostAddress( "255.255.255.0" ).toIPv4Address() ) )
        {
            returnMAC = mach;
        }
        else
            if (( addr.toIPv4Address()         & QHostAddress( "255.255.0.0" ).toIPv4Address() ) ==
                    ( m_VDRAddress.toIPv4Address() & QHostAddress( "255.255.0.0" ).toIPv4Address() ) )
            {
                returnMAC = mach;
            }
            else
                if (( addr.toIPv4Address()         & QHostAddress( "255.0.0.0" ).toIPv4Address() ) ==
                        ( m_VDRAddress.toIPv4Address() & QHostAddress( "255.0.0.0" ).toIPv4Address() ) )
                {
                    returnMAC = mach;
                }
    }

    if ( returnMAC.isNull() )
    {
        //Nothing found use a dummy address
        returnMAC = "00:00:00:00:00:01";
    }

    return returnMAC;
}


//------------------------------------------------
//     int b = vdrTCPSocket->bytesAvailable();
//
//     for (int i=0; i < b; i++)
//     {
//         quint8 a;
//         in >> a;
//         qDebug() << "in-value (#"<<i<<"): " << a << "  QChar: " << QChar(a);
//     }
//------------------------------------------------


//------------------------------------------------
//     for (int i=0; i < array.size(); i++)
//     {
//         qDebug()<<"Byte: " << i <<"  Value: " << (quint8)array.at(i);
//     }
//------------------------------------------------
